package com.sunyard.pcc.core.service.impl;

import com.sunyard.pcc.common.bo.*;
import com.sunyard.pcc.common.constant.ErrorCodeEnum;
import com.sunyard.pcc.common.constant.IsDeletedEnum;
import com.sunyard.pcc.common.constant.IsSuperAdminEnum;
import com.sunyard.pcc.common.constant.MenuTypeEnum;
import com.sunyard.pcc.common.exception.PccCommonException;
import com.sunyard.pcc.common.validator.ValidationUtils;
import com.sunyard.pcc.core.service.MenuService;
import com.sunyard.pcc.core.service.RoleService;
import com.sunyard.pcc.dal.mapper.ext.SysMenuExtMapper;
import com.sunyard.pcc.dal.model.SysMenuDO;
import com.sunyard.pcc.dal.query.SysMenuQuery;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: Hewei
 * @Description:
 * @Date: Created in 9:49 2018/3/7
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class MenuServiceImpl implements MenuService {

    @Autowired
    private SysMenuExtMapper sysMenuExtMapper;


    @Autowired
    private RoleService roleService;


    /**
     * 根据角色编号查询对应菜单集合
     *
     * @param roleIds
     * @return
     * @throws PccCommonException
     */
    @Override
    public List<SysMenuBO> queryMenusByRoleIDs(List<Long> roleIds) throws PccCommonException {

        ValidationUtils.isBlank(roleIds);

        List<SysMenuBO> sysMenuBOS = sysMenuExtMapper.selectMenusByRoleIds(roleIds);

        return sysMenuBOS;
    }


    /**
     * 根据用户编号查询对应菜单列表，无重复
     *
     * @param userID
     * @return
     * @throws PccCommonException
     */
    @Override
    public Set<Long> queryAllMenuIdsByUserId(Long userID) throws PccCommonException {

        ValidationUtils.isBlank(userID);

        List<SysRoleBO> sysRoleBOS = roleService.queryUserRoles(userID);

        List<Long> roleIds = new ArrayList<>();
        for (SysRoleBO sysRoleBO : sysRoleBOS) {
            roleIds.add(sysRoleBO.getId());
        }

        List<SysMenuBO> sysMenuBOS = queryMenusByRoleIDs(roleIds);

        Set<Long> menuIds = new TreeSet<>();
        for (SysMenuBO sysMenuBO : sysMenuBOS) {
            menuIds.add(sysMenuBO.getId());
        }

        return menuIds;
    }


    /**
     * 根据父菜单id查询子菜单列表
     *
     * @param menuId 父菜单Id
     * @return
     * @throws PccCommonException
     */
    @Override
    public List<SysMenuBO> selectChildrenMenuById(Long menuId) throws PccCommonException {
        //非空判断
        ValidationUtils.isBlank(menuId);

        SysMenuQuery sysMenuQuery = new SysMenuQuery();
        sysMenuQuery.createCriteria()
                .andParentMenuIdEqualTo(menuId)
                .andIsDeletedNotEqualTo(IsDeletedEnum.ISDELETED.getStatus());

        List<SysMenuDO> menus = sysMenuExtMapper.selectByQuery(sysMenuQuery);
        List<SysMenuBO> menuBOS = new ArrayList<>();
        for (SysMenuDO menu : menus) {
            SysMenuBO sysMenuBO = new SysMenuBO();
            BeanUtils.copyProperties(menu, sysMenuBO);
            menuBOS.add(sysMenuBO);
        }
        return menuBOS;
    }

    /**
     * 根据用户查询其所有菜单，按钮, 并生成树状结构结构
     *
     * @param sysUserBO
     * @return
     * @throws PccCommonException
     */
    @Override
    public List<SysMenuBO> getUserMenuList(SysUserBO sysUserBO) throws PccCommonException {

        ValidationUtils.isBlank(sysUserBO);

        //如果用户为超级管理员, 查询所有菜单并生成对应数据
        if (IsSuperAdminEnum.ISDELETED.getStatus().equals(sysUserBO.getIsSuperAdmin())) {
            return queryAllList(null);
        }

        Set<Long> userMenuIds = queryAllMenuIdsByUserId(sysUserBO.getId());

        return queryAllList(userMenuIds);

    }


    /**
     * 校验用户是否有该菜单对应URL的访问权限
     *
     * @param menu       特定菜单
     * @param sysMenuBOS 用户拥有权限的菜单列表
     * @return
     * @throws PccCommonException
     */
    @Override
    public Boolean hasPermissionToMenu(SysMenuBO menu, List<SysMenuBO> sysMenuBOS) throws PccCommonException {
        String path = menu.getUrl();
        String method = menu.getPerms();
        Boolean result = false;
        Boolean kidResult = false;
        for (SysMenuBO sysMenuBO : sysMenuBOS) {
            String menuPath = sysMenuBO.getUrl();
            String menuMethod = sysMenuBO.getPerms();
            result = path.equals(menuPath) && method.equalsIgnoreCase(menuMethod);
            if (result) {
                return true;
            }
            List<SysMenuBO> kidSysMenuBO = sysMenuBO.getChildMenus();
            if (kidSysMenuBO != null) {
                kidResult = hasPermissionToMenu(menu, kidSysMenuBO);
            }
            if (kidResult) {
                return true;
            }
        }

        return false;
    }


    /**
     * 添加新菜单数据
     *
     * @param menu 新菜单数据
     * @return
     */
    @Override
    public Boolean addNewMenu(SysMenuBO menu) throws PccCommonException {

        ValidationUtils.isBlank(menu);
        Boolean result = true;
        //查寻本组菜单下的最大sort
        Integer sort = sysMenuExtMapper.selectMaxSortByParentId(menu.getParentMenuId());
        menu.setSort(sort + 1);
        menu.setIsDeleted(IsDeletedEnum.UNDELETED.getStatus());
        SysMenuDO sysMenuDO = new SysMenuDO();
        BeanUtils.copyProperties(menu, sysMenuDO);
        int i = sysMenuExtMapper.insertSelective(sysMenuDO);
        if (i != 1) {
            throw new PccCommonException(ErrorCodeEnum.INSERT_ERROR);
        }
        return result;
    }


    /**
     * 删除id对应菜单
     *
     * @param menuId
     * @return
     * @throws PccCommonException
     */
    @Override
    public Boolean deleteMenuById(Long menuId) throws PccCommonException {
        //非空校验
        ValidationUtils.isBlank(menuId);

        //判断该菜单下是否有子菜单
        List<SysMenuBO> childMenus = selectChildrenMenuById(menuId);
        if (!CollectionUtils.isEmpty(childMenus)) {
            throw new PccCommonException(ErrorCodeEnum.MENU_HAS_CHILD_MENU_ERROR);
        }


        //软删除删除对应菜单
        SysMenuQuery sysMenuQuery = new SysMenuQuery();
        sysMenuQuery.createCriteria()
                .andIdEqualTo(menuId)
                .andIsDeletedNotEqualTo(IsDeletedEnum.ISDELETED.getStatus());

        SysMenuDO updateMenu = new SysMenuDO();
        updateMenu.setId(menuId);
        updateMenu.setIsDeleted(IsDeletedEnum.ISDELETED.getStatus());
        int count = sysMenuExtMapper.updateByQuerySelective(updateMenu, sysMenuQuery);
        if (count == 1) {
            return true;
        } else {
            throw new PccCommonException(ErrorCodeEnum.DATABASE_DATA_ERROR);
        }
    }


    /**
     * 更新对应id的menu数据
     *
     * @param menuId    menu编号
     * @param sysMenuBO 新菜单编号
     * @return
     * @throws PccCommonException
     */
    @Override
    public Boolean updateMenu(Long menuId, SysMenuBO sysMenuBO) throws PccCommonException {
        //非空校验
        ValidationUtils.isBlank(menuId, sysMenuBO);

        //更新数据设置
        SysMenuDO menuDO = new SysMenuDO();
        BeanUtils.copyProperties(sysMenuBO, menuDO);
        menuDO.setId(menuId);

        int result = sysMenuExtMapper.updateByPrimaryKeySelective(menuDO);
        if (result != 1) {
            throw new PccCommonException(ErrorCodeEnum.INSERT_ERROR);
        }

        return true;

    }


    /**
     * 查询 菜单列表 树形结构
     *
     * @return
     * @throws PccCommonException
     */
    @Override
    public List<SysMenuBO> getMenuListInTreeMode() throws PccCommonException {

        //所有菜单查询, 按菜单类型进行排列
        List<SysMenuBO> allMenus = sysMenuExtMapper.selectAllMenus();

        //目录列表
        List<SysMenuBO> catalogs = queryMenuByType(allMenus, MenuTypeEnum.CATALOG);
        //菜单列表
        List<SysMenuBO> menuList = queryMenuByType(allMenus, MenuTypeEnum.MENU);
        //按钮列表
        List<SysMenuBO> buttonList = queryMenuByType(allMenus, MenuTypeEnum.BUTTON);


        //菜单下子按钮设置
        for (SysMenuBO menu : menuList) {
            //菜单下的子按钮列表
            List<SysMenuBO> childButtons = buttonList.stream()
                                                    .filter(button -> button.getParentMenuId().equals(menu.getId()))
                                                    .collect(Collectors.toList());
            menu.setChildMenus(childButtons);
        }

        //目录下子菜单设置
        for (SysMenuBO catalog : catalogs) {
            //目录下的子菜单列表
            List<SysMenuBO> childMenus = menuList.stream()
                                                .filter(menu -> menu.getParentMenuId().equals(catalog.getId()))
                                                .collect(Collectors.toList());
            catalog.setChildMenus(childMenus);
        }

        return catalogs;
    }


    /**
     * 查询菜单列表
     * @return
     * @throws PccCommonException
     */
    @Override
    public List<SysMenuBO> getMenuList() throws PccCommonException {
        return sysMenuExtMapper.selectAllMenus();
    }

    /**
     * 递归查询目录列表下共有几条菜单数据
     * @param menus
     * @return
     * @throws PccCommonException
     */
    @Override
    public Integer menuListCounts(List<SysMenuBO> menus) throws PccCommonException {
        Integer count = menus.size();
        for (SysMenuBO menu : menus) {
            List<SysMenuBO> child = menu.getChildMenus();
            if (!CollectionUtils.isEmpty(child)) {
                count += menuListCounts(child);
            }
        }
        return count;
    }

    /**
     * 根据菜单类型删选菜单列表
     *
     * @param menus
     * @param typeEnum
     * @return
     */
    private List<SysMenuBO> queryMenuByType(List<SysMenuBO> menus, MenuTypeEnum typeEnum) {
        return menus.stream()
                .filter(sysMenuBO -> typeEnum.getStatus().equals(sysMenuBO.getMenuType()))
                .collect(Collectors.toList());
    }

    /**
     * 条件查询菜单总数
     *
     * @param sysMenuQueryBO
     * @return
     * @throws PccCommonException
     */
    @Override
    public Integer getCountByRequirementInTreeMode(SysMenuQueryBO sysMenuQueryBO) throws PccCommonException {
        //非空校验
        ValidationUtils.isBlank(sysMenuQueryBO);
        //父级菜单查询， 此处分页查询参数只针对父级菜单
        Map<String, Object> parentMenuListQueryMap = menuListQueryMap(sysMenuQueryBO, false);
        Integer count = sysMenuExtMapper.selectCountInTreeMode(parentMenuListQueryMap);
        return count;
    }


    private List<SysMenuBO> queryAllList(Set<Long> menuIds) {
        //查询根菜单列表
        List<SysMenuBO> rootMenuList = queryParentsMenus(0L, menuIds);

        return getMenuTreeList(rootMenuList, menuIds);
    }


    /**
     * 根据父级菜单Id, 查询符合条件的菜单集合
     *
     * @param parentId 父级菜单ID
     * @param menuIds  用户可访问的菜单集合，用于筛选结果
     * @return
     */
    private List<SysMenuBO> queryParentsMenus(Long parentId, Set<Long> menuIds) {

        SysMenuQuery sysMenuQuery = new SysMenuQuery();
        sysMenuQuery.createCriteria()
                .andParentMenuIdEqualTo(parentId)
                .andIsDeletedEqualTo(IsDeletedEnum.UNDELETED.getStatus());

        List<SysMenuDO> parentMenus = sysMenuExtMapper.selectByQuery(sysMenuQuery);

        if (menuIds == null) {
            return DO2BO(parentMenus);
        }

        List<SysMenuDO> newParentMenus = new ArrayList<>();
        for (SysMenuDO sysMenuDO : parentMenus) {
            if (menuIds.contains(sysMenuDO.getId())) {
                newParentMenus.add(sysMenuDO);
            }
        }

        return DO2BO(newParentMenus);

    }


    /**
     * 利用递归，根据父级菜单，查询子菜单并生成树形结构
     *
     * @param sysMenuBOS 复制菜单列表
     * @param menuIds    用户可访问的菜单
     * @return
     */
    private List<SysMenuBO> getMenuTreeList(List<SysMenuBO> sysMenuBOS, Set<Long> menuIds) {
        List<SysMenuBO> result = new ArrayList<>();
        for (SysMenuBO sysMenuBO : sysMenuBOS) {
            //只对目录或菜单进行循环
            if (MenuTypeEnum.CATALOG.getStatus().equals(sysMenuBO.getMenuType())
                    || MenuTypeEnum.MENU.getStatus().equals(sysMenuBO.getMenuType())) {

                sysMenuBO.setChildMenus(
                        getMenuTreeList(queryParentsMenus(sysMenuBO.getId(), menuIds), menuIds)
                );
            }
            result.add(sysMenuBO);
        }
        return result;
    }


    /**
     * DO集合转换为BO集合
     *
     * @param sysMenuDOS
     * @return
     */
    private List<SysMenuBO> DO2BO(List<SysMenuDO> sysMenuDOS) {
        List<SysMenuBO> sysMenuBOS = new ArrayList<>();
        for (SysMenuDO sysMenuDO : sysMenuDOS) {
            SysMenuBO sysMenuBO = new SysMenuBO();
            BeanUtils.copyProperties(sysMenuDO, sysMenuBO);
            sysMenuBOS.add(sysMenuBO);
        }
        return sysMenuBOS;
    }


    /**
     * 列表查询条件生成
     *
     * @param sysMenuQueryBO
     * @param pageOffset     是否分页
     * @return
     */
    private Map<String, Object> menuListQueryMap(SysMenuQueryBO sysMenuQueryBO, Boolean pageOffset) {
        Map<String, Object> query = new HashMap<>(64);

        query.put("menuName", sysMenuQueryBO.getMenuName());
        query.put("url", sysMenuQueryBO.getUrl());
        query.put("method", sysMenuQueryBO.getMethod());
        query.put("parentId", sysMenuQueryBO.getParentId());

        if (pageOffset) {
            query.put("pageSize", sysMenuQueryBO.getPageSize());
            //计算页偏移
            SysMenuQuery sysMenuQuery = new SysMenuQuery();
            sysMenuQuery.setPageNo(sysMenuQueryBO.getPageNo());
            sysMenuQuery.setPageSize(sysMenuQueryBO.getPageSize());
            query.put("pageOffset", sysMenuQuery.getPageOffset());
        }
        return query;
    }

}
